package com.szsh.aiot.framework.aspectj;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.szsh.aiot.common.annotation.CustomPermission;
import com.szsh.aiot.common.core.domain.AjaxResult;
import com.szsh.aiot.common.core.domain.Key;
import com.szsh.aiot.common.core.domain.RequestDto;
import com.szsh.aiot.common.enums.EncryptMethodType;
import com.szsh.aiot.common.utils.EncryptUtil;
import com.szsh.aiot.common.utils.StringUtils;
import com.szsh.aiot.framework.constant.Constants;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * 操作日志记录处理
 */
@Aspect
@Component
@Order(2)
public class CustomPermissionAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(CustomPermissionAspect.class);

//    private final StringRedisTemplate stringRedisTemplate;

    private final HashOperations<String, String, String> stringHashOperations;

    private final Gson gson;

    public CustomPermissionAspect(StringRedisTemplate stringRedisTemplate, Gson gson) {
//        this.stringRedisTemplate = stringRedisTemplate;
        this.stringHashOperations = stringRedisTemplate.opsForHash();
        this.gson = gson;
    }

    /**
     * 处理日志注解
     * @param joinPoint 切点
     */
    @Around(value = "@annotation(com.szsh.aiot.common.annotation.CustomPermission) && @annotation(customPermission)")
    public Object around(ProceedingJoinPoint joinPoint, CustomPermission customPermission) {
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 请求实体
        RequestDto requestDto = null;
        for (Object obj : args) {
            if (null == obj) {
                continue;
            }
            if (RequestDto.class.isAssignableFrom(obj.getClass())) {
                requestDto = (RequestDto) obj;
            }
        }

        Key key = null;
        Object returnObject = null;
        if (null != requestDto && customPermission.isValidateRequestDto()) {
            // 有传入参数 且 需要验证
            returnObject = handleRequestDto(requestDto, customPermission);
            key = requestDto.getKey();
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("[处理后requestDto]{}", requestDto);
            }
        }

        if (null == returnObject) {
            // 无需验证参数 或者 参数验证成功
            try {
                // 执行方法
                returnObject = joinPoint.proceed(args);
                // 统一加密
                if (null != returnObject && customPermission.isEncryptAjaxResultData()
                        && AjaxResult.class.isAssignableFrom(returnObject.getClass())) {
                    // 需要 处理返回的data字段
                    AjaxResult ajaxResult = ((AjaxResult) returnObject);
                    if (LOGGER.isInfoEnabled()) {
                        LOGGER.info("[原始返回数据]{}", new Gson().toJson(ajaxResult.get(AjaxResult.DATA_TAG)));
                    }
                    returnObject = handleAjaxResult(ajaxResult, key);
                }
            } catch (Throwable e) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[系统异常]", e);
                }

                Signature signature = joinPoint.getSignature();
                if (signature instanceof MethodSignature) {
                    Class<?> methodReturnType = ((MethodSignature) signature).getReturnType();
                    if (AjaxResult.class.isAssignableFrom(methodReturnType)) {
                        // 返回 AjaxResult 类型
                        returnObject = AjaxResult.error();
                    }
                }
            }
        }

        // 统一设置 sequenceNo 字段
        if (null != requestDto) {
            if (null != returnObject && AjaxResult.class.isAssignableFrom(returnObject.getClass())) {
                ((AjaxResult) returnObject).put("sequenceNo", requestDto.getSequenceNo());
            }
        }
        return returnObject;
    }

    /**
     * 验证requestDto
     * @param requestDto 需要验证的requestDto
     * @return 验证结果. null:表示验证成功
     */
    private AjaxResult handleRequestDto(RequestDto requestDto, CustomPermission customPermission) {
        // 请求流水号
        String sequenceNo = requestDto.getSequenceNo();
        // 应用ID
        String appId = requestDto.getAppId();
        // 密钥版本
        String version = requestDto.getVersion();
        // 请求密文
        String data = requestDto.getData();
        // 签名字段
        String signature = requestDto.getSignature();

        // 参数验证
        if (StringUtils.isBlank(sequenceNo)) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[请求参数sequenceNo为空]");
            }
            return AjaxResult.badRequest("请求参数sequenceNo为空");
        }
        if (StringUtils.isBlank(appId)) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[请求参数appId为空]");
            }
            return AjaxResult.badRequest("请求参数appId为空");
        }
        if (StringUtils.isBlank(version)) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[请求参数version为空]");
            }
            return AjaxResult.badRequest("请求参数version为空");
        }
        if (StringUtils.isBlank(data)) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[请求参数data为空]");
            }
            return AjaxResult.badRequest("请求参数data为空");
        }
        if (StringUtils.isBlank(signature)) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[请求参数signature为空]");
            }
            return AjaxResult.badRequest("请求参数signature为空");
        }

        Key key = null;
        try {
            // 根据 appId 和 version 获取密钥
            String keyStr = StringEscapeUtils.unescapeJava(stringHashOperations.get(Constants.REDIS_KEY_PREFIX + appId, version));
            LOGGER.info("根据key:{}从缓存中读取的加密参数{}",Constants.REDIS_KEY_PREFIX + appId+version,keyStr);

            if (StringUtils.isBlank(keyStr)) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[根据appId和version获取密钥失败]");
                }
                return AjaxResult.unauthorized("根据appId和version获取密钥失败");
            }


            key = gson.fromJson(keyStr, Key.class);
          /*  key.setAppId("289a1c91357a44f2851b481acc68fbde");
            key.setVersion("1.0");
            key.setSignatureSecret("uS4EbP9nZ6");
            key.setEncryptMethodType(1);
            key.setKey("CcFOSZeWVmPw+EHALSlDqg==");*/
        } catch (Exception e) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[根据appId和version获取密钥解密失败]", e);
            }
            return AjaxResult.unauthorized("根据appId和version获取密钥失败");
        }

        if (null == key) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[根据appId和version获取密钥转换失败]");
            }
            return AjaxResult.unauthorized("根据appId和version获取密钥转换失败");
        }

        // 设置密钥
        requestDto.setKey(key);

        // 计算签名
        String calcSignature =  DigestUtils.md5Hex(sequenceNo + data + key.getSignatureSecret());
        if (!signature.equalsIgnoreCase(calcSignature)) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("[签名验证失败].signature:[{}].calcSignature:[{}]", signature, calcSignature);
            }
            return AjaxResult.unauthorized("签名验证失败");
        }

        JsonObject requestJsonObject;
        if (customPermission.isEncryptRequestDtoData()) {
            // 请求参数加密
            try {
                // 解密后数据
                String decryptData;
                Integer encryptMethodType = key.getEncryptMethodType();
                if (Objects.equals(EncryptMethodType.AES_ECB_PKCS_5.value(), encryptMethodType)) {
                    // AES/ECB/PKCS5Padding
                    decryptData = EncryptUtil.decryptAESByBase64Key(data, key.getKey());
                } else if (Objects.equals(EncryptMethodType.AES_CBC_PKCS_5.value(), encryptMethodType)) {
                    // AES/CBC/PKCS5Padding
                    decryptData = EncryptUtil.decryptAESByBase64Key(data, key.getKey(), key.getIv());
                } else if (Objects.equals(EncryptMethodType.DES.value(), encryptMethodType)) {
                    // des
                    decryptData = EncryptUtil.decryptDES(data, key.getKey());
                } else if (Objects.equals(EncryptMethodType.RSA.value(), encryptMethodType)) {
                    // rsa
                    // 根据私钥解密
                    decryptData = EncryptUtil.decryptByPrivateKey(data, key.getPrivateKey());
                } else {
                    // 未知加密类型
                    return AjaxResult.unauthorized("未知加密类型");
                }
                // 设置解密数据
                requestJsonObject = gson.fromJson(decryptData, JsonObject.class);

            } catch (Exception e) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[根据appId和version获取密钥解密失败]", e);
                }
                return AjaxResult.unauthorized("解密失败");
            }
        } else {
            // 请求参数没有加密
            try {
                requestJsonObject = gson.fromJson(data, JsonObject.class);
            } catch (Exception e) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[请求参数转成Json失败]", e);
                }
                return AjaxResult.unauthorized("请求参数转成Json失败");
            }
        }

        if (null != requestJsonObject) {
            if (!requestJsonObject.has("sequenceNo")) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[requestJsonObject字段][缺少sequenceNo参数]");
                }
                return AjaxResult.badRequest("缺少sequenceNo参数");
            }
            JsonElement jsonElement = requestJsonObject.get("sequenceNo");
            if (!jsonElement.isJsonPrimitive()) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[requestJsonObject字段][sequenceNo字段json格式错误]");
                }
                return AjaxResult.badRequest("sequenceNo格式错误");
            }
            String dataSequenceNo = jsonElement.getAsString();
            if (StringUtils.isBlank(dataSequenceNo)) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[requestJsonObject字段][sequenceNo为空]");
                }
                return AjaxResult.badRequest("sequenceNo为空");
            }
            if (!Objects.equals(dataSequenceNo, sequenceNo)) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[sequenceNo和dataSequenceNo不同].sequenceNo:[{}],dataSequenceNo:[{}]",
                            sequenceNo, dataSequenceNo);
                }
                return AjaxResult.badRequest("sequenceNo不同");
            }

            if (!requestJsonObject.has("timestamp")) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("[requestJsonObject字段][缺少timestamp参数]");
                }
                return AjaxResult.badRequest("缺少timestamp参数");
            }

            // 是否验证 tyAccount
            if (customPermission.isValidateTyAccount()) {
                // 需要验证 tyAccount
                if (!requestJsonObject.has("tyAccount")) {
                    if (LOGGER.isErrorEnabled()) {
                        LOGGER.error("[requestJsonObject字段][缺少tyAccount参数]");
                    }
                    return AjaxResult.badRequest("缺少tyAccount参数");
                }
                jsonElement = requestJsonObject.get("tyAccount");
                if (!jsonElement.isJsonPrimitive()) {
                    if (LOGGER.isErrorEnabled()) {
                        LOGGER.error("[requestJsonObject字段][tyAccount字段json格式错误]");
                    }
                    return AjaxResult.badRequest("tyAccount格式错误");
                }
                String tyAccount = jsonElement.getAsString();
                if (StringUtils.isBlank(tyAccount)) {
                    if (LOGGER.isErrorEnabled()) {
                        LOGGER.error("[requestJsonObject字段][tyAccount为空]");
                    }
                    return AjaxResult.badRequest("tyAccount为空");
                }
                if (!tyAccount.matches(Constants.PHONE_REG)) {
                    if (LOGGER.isErrorEnabled()) {
                        LOGGER.error("[requestJsonObject字段][tyAccount格式错误]");
                    }
                    return AjaxResult.badRequest("tyAccount格式错误");
                }
            }

            requestDto.setRequestJsonElement(requestJsonObject);
        }
        // 验证成功
        return null;
    }
    /**
     * 将AjaxResult字段data加密
     * @param ajaxResult 需要处理的AjaxResult
     * @param key 密钥信息
     * @return 处理后的AjaxResult
     */
    public AjaxResult handleAjaxResult(AjaxResult ajaxResult, Key key) throws Exception {
        if (null == key) {
            return ajaxResult;
        }
        Object dataObject = new Gson().toJson(ajaxResult.get(AjaxResult.DATA_TAG));
        if (null != dataObject) {
            String resultData;
            String data = dataObject.toString();
            Integer encryptMethodType = key.getEncryptMethodType();
            if (Objects.equals(EncryptMethodType.AES_ECB_PKCS_5.value(), encryptMethodType)) {
                // AES/ECB/PKCS5Padding
                resultData = EncryptUtil.encryptAESBase64String(data, key.getKey());
            } else if (Objects.equals(EncryptMethodType.AES_CBC_PKCS_5.value(), encryptMethodType)) {
                // AES/CBC/PKCS5Padding
                resultData = EncryptUtil.encryptAESBase64String(data, key.getKey(), key.getIv());
            } else if (Objects.equals(EncryptMethodType.DES.value(), encryptMethodType)) {
                // des
                resultData = EncryptUtil.encryptDES(data, key.getKey());
            } else if (Objects.equals(EncryptMethodType.RSA.value(), encryptMethodType)) {
                // rsa
                // 根据私钥加密
                resultData = EncryptUtil.encryptByPrivateKey(data, key.getPrivateKey());
            } else {
                resultData = data;
            }
            // 替换掉
            ajaxResult.put(AjaxResult.DATA_TAG, resultData);
        }
        return ajaxResult;
    }
}
